/**
  ******************************************************************************
  * @file    message_queue.c
  * @author  ashuai0110
  * @version V2.4
  * @date    2024-09-10
  * @brief   消息队列
  *          (适合不定长数据和复杂数据,写入时动态申请内存)
  *
  ******************************************************************************
  * @attention
  *
  * 版权声明:内容为编者(ashuai0110)原创,使用请注明出处,当然,你也可以不这样做^_^
  * 出处链接:https://gitee.com/ashuai0110/mcu_reuse_development_module.git
  *
  * Change Logs:
  * Date          Author        Notes
  * 2023-10-27    ashuai0110    完成基本内容
  * 2023-12-30    ashuai0110    修改内部链表逻辑
  * 2024-09-04    ashuai0110    增加临界区控制,删除一些assert
  *
  ******************************************************************************
  */

/* 包含头文件-----------------------------------------------------------------*/
#include "message_queue.h"

/**
 * @addtogroup modules
 * @{
 */

/**
 * @defgroup message_queue message_queue
 * @{
 */

/* 私有宏定义-----------------------------------------------------------------*/
/**
 * @defgroup message_queue_local_macros message_queue local macros
 * @{
 */

/**
 * @defgroup message_queue_check_parameters_validity message_queue check parameters validity
 * @{
 */
/* 消息队列数据大小有效性检查 */
#define IS_VALID_QA_SIZE(x)                                                   \
(       (x) != 0u)
/**
 * @}
 */

/**
 * @}
 */

/* 私有类型定义---------------------------------------------------------------*/

/* 私有变量-------------------------------------------------------------------*/

/* 全局变量-------------------------------------------------------------------*/

/* 私有函数原型---------------------------------------------------------------*/

/**
 * @defgroup message_queue_global_functions message_queue global functions
 * @{
 */

/**
  * @brief  消息队列初始化
  *
  * @param  _mq  : 消息队列实例
  *
  * @param  size : 消息队列消息数量
  *
  * @retval None
  */
void mes_queue_init(mes_queue_t *_mq, uint32_t size)
{
    MW_ASSERT_PARAM(IS_VALID_POINTER(_mq));
    MW_ASSERT_PARAM(IS_VALID_QA_SIZE(size));

    /* 初始化各参数 */
    memset(_mq, 0, sizeof(mes_queue_t));
    _mq->maxCnt = size;
}

/**
  * @brief  消息队列取消初始化(删除)
  *
  * @note   将消息队列内所有消息占用内存释放
  *
  * @param  _mq : 消息队列实例
  *
  * @retval None
  */
void mes_queue_de_init(mes_queue_t *_mq)
{
    mes_queue_item_t *pItem;

    /* 遍历链表 释放所有消息的内存 */
    while(NULL != _mq->pHead)
    {
        pItem = _mq->pHead;
        _mq->pHead = _mq->pHead->pNext;
        MQ_FREE_FUNC(pItem);
    }
    /* 置零所有参数 */
    memset(_mq, 0, sizeof(mes_queue_t));
}

/**
  * @brief  向消息队列写入数据
  *
  * @param  _mq         : 消息队列实例
  *
  * @param  pData       : 写入的数据
  *
  * @param  size        : 写入的数据大小byte
  *
  * @retval 返回写入结果
  *    @arg MW_RET_OK   : 成功
  *    @arg MW_RET_ERR  : 动态申请内存失败,写入失败
  *    @arg MW_RET_FULL : 消息队列已满(不可写)
  */
uint8_t mes_queue_write(mes_queue_t *_mq, void *pData, uint32_t size)
{
    uint8_t ret = MW_RET_FULL;
    mes_queue_item_t **pItem;

    /* 当前数量小于最大数量则说明消息队列不满可写入 */
    if(_mq->curCnt < _mq->maxCnt)
    {
        MW_CRITICAL_LOCK();
        pItem = &_mq->pHead; /* 先拿到头节点的地址 */
        /* 遍历寻找空节点 */
        while(NULL != *pItem)
        {
            pItem = &(*pItem)->pNext;
        }
        *pItem = (mes_queue_item_t *)MQ_MALLOC_FUNC(sizeof(mes_queue_item_t) + size);
        /* 申请内存不为空 */
        if(NULL != *pItem)
        {
            /* 将数据和数量拷贝到尾结点 */
            (*pItem)->pNext = NULL;
            (*pItem)->size = size;
            memcpy((void *)(*pItem)->data, pData, size);
            _mq->curCnt = _mq->curCnt + 1; /* 消息计数+1 */
            ret = MW_RET_OK;
        } /* 否则返回失败 */
        else
        {
            ret =  MW_RET_ERR;
        }
        MW_CRITICAL_UNLOCK();
    }

    return ret;
}

/**
  * @brief  从消息队列读出数据
  *
  * @param  _mq        : 消息队列实例
  *
  * @param  pBuf       : 读出的数据
  *
  * @param  size       : 读出的数据大小byte
  *
  * @retval 返回读出结果
  *    @arg MW_RET_OK  : 成功
  *    @arg MW_RET_ERR : 消息队列为空(不可读)
  */
uint8_t mes_queue_read(mes_queue_t *_mq, void *pBuf, uint32_t *size)
{
    mes_queue_item_t *pItem;

    /* 头节点为空或当前计数值为0则表示消息队列为空 */
    if(NULL == _mq->pHead || 0 == _mq->curCnt)
    {
        return MW_RET_ERR;
    }

    MW_CRITICAL_LOCK();
    pItem = _mq->pHead; /* 拿到头节点 */
    /* 将数据和数量拷贝到外部缓存区 */
    *size = _mq->pHead->size;
    memcpy(pBuf, (void *)_mq->pHead->data, _mq->pHead->size);
    _mq->pHead = _mq->pHead->pNext; /* 头节点指向下一节点 */
    MQ_FREE_FUNC(pItem); /* 释放内存 */
    _mq->curCnt = _mq->curCnt - 1; /* 消息计数-1 */
    MW_CRITICAL_UNLOCK();

    return MW_RET_OK;
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */
